<?php
declare (strict_types = 1);

namespace app\user\model;

use think\facade\Db;
use think\Model;

/**
 * @mixin \think\Model
 */
class UserInvite extends Model
{
    public $error = '';//异常

    // 查询指定层级
    public function getOne($pid){
        return $this->where('pid',$pid)->select();
    }

    // 查询到所有的上下级关系
    public function getListAll(){
        $data =  $this->select()->toArray();
        $linshiData = [];
        // 拿出键值
        foreach($data as $k => $v){
            $v['son'] = [];
            $linshiData[$v['uid']] = $v;
        }
        return $this->genTree9($linshiData);
    }

    // 递归
    /**
     * 键值对形式 转换成uid  成为数组键值  请参考函数 getListAll
     * @param $items
     * @return array
     */
    function genTree9($items) {
        $tree = array(); // 格式化好的树
        foreach ($items as $item)
            if (isset($items[$item['upid']])){
                $items[$item['upid']]['son'][ $item['uid'] ] = &$items[$item['uid']];
            } else {
                $tree[] = &$items[$item['uid']];
            }
        return $tree;
    }


    /*
     * 绑定用户关系
     * array $user   被邀请人 数据包含用户id和等级level和invite_id邀请人id字段
     * array $invite 邀请人  数据包含用户id和等级level
     */
    public function bindInvite($user, $invite)
    {
        try{
            if(!empty($user['invite_id'])) exception('绑定失败：已绑定邀请人!');
            if(empty($user)||empty($invite)) exception('绑定失败：参数有误!');
            if($invite['id'] == $user['id']) exception('绑定失败：不能绑定自己!');
            if($invite['level'] < 1) exception('绑定失败!');#游客无权限
            if($invite['level'] < $user['level']) exception('绑定失败：邀请人等级比自己低，无法接受邀请!');
            $info = $this->where(['upid'=>$user['id'],'uid'=>$invite['id']])->find();
            if(!empty($info)) exception('绑定失败：不能接受自己的下级邀请!');
            $check = $this->checkPid($user['id'],$invite['id']);
            if($check == false) exception('绑定失败：与邀请人存在关系链，无法绑定!');
            $upidModel = $this->getTop($invite['id']);//获取用户上级信息
            $foot = $this->getFoot($user['id']);//获取用户下级信息
            $team_pid = isset($upidModel['pid']) ? $upidModel['pid'] : 0;//团队层级
            $top_id = !empty($upidModel['topId'])?$upidModel['topId']:$invite['id'];//最上级id
            $pid = isset($upidModel['pid']) ? $upidModel['pid'] + 1 : 1;//用户单曲
            $this::startTrans();
            if(!empty($foot)){
                //存在下级用户
                $team_pid += $foot['pid'];
                if($pid > 0){
                    $this->inviteIncPid($user['id'],1,$pid,$upidModel);//所有下级pid+1
                }
                $this->where(['top_id'=>$user['id']])->update(['top_id'=>$top_id,'team_pid'=>$team_pid]);
            }

            $this->where(['top_id'=>$upidModel['topId']])->update(['team_pid'=>$team_pid]);
            $this->insert([
                'uid' => $user['id'],
                'pid' => isset($upidModel['pid']) ? $upidModel['pid'] + 1 : 1,
                'upid' => $invite['id'],
                'top_id' => $top_id,
                'team_pid' => $team_pid,
                'add_time' => time()
            ]);
            $this::commit();
            return true;
        }catch (\Exception $e){
            $this::rollback();
            if(stristr($e->getMessage(),'SQLSTATE')){
                $this->error = '数据走丢了，请稍后再试！';
            }else $this->error = $e->getMessage();
            return false;
        }
    }


    /*
     * 绑定用户关系
     * array $user   被邀请人 数据包含用户id和等级level和invite_id邀请人id字段
     * array $invite 邀请人  数据包含用户id和等级level
     */
    public function bindInviteTT($user, $invite)
    {
        try{
            if(!empty($user['invite_id'])) exception('绑定失败：已绑定邀请人!');
            if(empty($user)||empty($invite)) exception('绑定失败：参数有误!');
            if($invite['id'] == $user['id']) exception('绑定失败：不能绑定自己!');
            if($invite['level'] < 1) exception('绑定失败!');#游客不能邀请新用户
            if($invite['level'] < $user['level']) exception('绑定失败：邀请人等级比自己低，无法接受邀请!');
            $info = $this->where(['uid'=>$user['id']])->find();
            if(!empty($info['upid'])){
                if($info['upid'] == $invite['id']) exception('绑定失败：不能接受自己的下级邀请!');
                else exception('绑定失败：已绑定邀请人!');
            }
            $check = $this->checkPid($user['id'],$invite['id']);
            if($check == false) exception('绑定失败：与邀请人存在关系链，无法绑定!');
            $upidModel = $this->where(['uid'=>$invite['id']])->find();//获取用户上级信息
            $foot = $this->where(['upid'=>$user['id']])->count();//获取用户下级
            $pid = isset($upidModel['pid']) ? $upidModel['pid'] + 1 : 1;//用户当前层级
            $this::startTrans();
            if(!empty($foot)){
                //存在下级用户
                if($pid > 0) {
                    $type = 1;
                    if ($info['pid'] > 0){
                        if($info['pid'] > $pid){
                            $num = $pid;
                            $type = 2;
                        }else $num = $pid - $info['pid'];
                    }else
                        $num = $pid;
                    $this->inviteIncPid($user['id'], $type, $num, $upidModel);//所有下级pid,level更新
                }
            }
            $this->where(['uid' => $user['id']])->update([
                'pid' => $pid,
                'upid' => $invite['id'],
                'level' => (empty($upidModel['level'])) ? $invite['id'] : ($upidModel['level'].','.$upidModel['uid']),
                'add_time' => time()
            ]);
            $this::commit();
            return true;
        }catch (\Exception $e){
            $this::rollback();
            /*if(stristr($e->getMessage(),'SQLSTATE')){
                $this->error = '数据走丢了，请稍后再试！';
            }else*/ $this->error = $e->getMessage();
            return false;
        }
    }

    /*
     * 获取该用户所有下级(递归)
     */
    public function loopInvite($uid){
        $map = $this->where(['upid'=>$uid])->select()->toArray();
        if(empty($map)) return [];
        foreach($map as $k => $v)
        {
            $item = $this->loopInvite($v['uid']);
            if($item)
                $map = array_merge($map,$item);
        }
        return $map;
    }

    /*
     * 获取该用户所有上级(递归)
     */
    public function loopTop($uid){
        $map = $this->alias('i')->join('user u','u.id=i.uid','left')->where(['i.uid'=>$uid])->field('i.uid,i.upid,i.pid,u.level,u.binding_time')->select();
        if(empty($map[0])) return [];
        $map = $map->toArray();
        $item = $this->loopTop($map[0]['upid']);
        if($item)
            $map = array_merge($map, $item);
        return $map;
    }

    /*
     * 获取该用户所有上级(递归)
     */
    public function loopTopPid($uid){
        $map = $this->where(['uid'=>$uid])->field('uid,upid,pid')->select();
        if(empty($map[0])) return [];
        $map = $map->toArray();
        $item = $this->loopTopPid($map[0]['upid']);
        if($item)
            $map = array_merge($map,$item);
        return $map;
    }

    /*
     * 获取该用户所有上级(递归)
     */
    public function checkPid($uid,$pid){
        $map = $this->loopTopPid($pid);
        if(empty($map)) return true;
        $ids = array_column($map,'upid');
        $map = array_merge([$pid],$ids);
        if(in_array($uid,$map)) return false;
        return true;
    }

    /*
     * 所有下级pid+1
     */
    public function inviteIncPid($id,$type,$num,$upid){
        $map = $this->loopInvite($id);
        if(empty($map)) return true;
        $map = array_column($map,'uid');//获取所有下级id
        $level = (empty($upid['level']))? $upid['uid'] : ($upid['level'].','.$upid['uid']);
        if(count($map) > 2000){
            //大于2000条记录则分次执行   Db::raw("REPLACE(level, IF(LOCATE('".$id.",',level)>0,SUBSTRING(level,1,LOCATE('".$id."',level)+LENGTH('".$id."')-1),SUBSTRING(level,1)) ,".$level.")")
            $map = array_chunk($map, 2000, true);
            foreach($map as $v){
                $ids = implode(',',$v);
                if($type == 1)
                    $this->whereIn('uid',$ids)->inc('pid',$num)->update(['level'=>Db::raw("IF(LOCATE(".$id.",level)>0,CONCAT('".$level.",',".Db::raw('level')."),'".$level."')")]);
                else
                    $this->whereIn('uid',$ids)->dec('pid',$num)->update(['level'=>Db::raw("IF(LOCATE(".$id.",level)>0,CONCAT('".$level.",',".Db::raw('level')."),'".$level."')")]);
            }
        }else
            if($type == 1)
                $this->whereIn('uid',implode(',',$map))->inc('pid',$num)->update(['level'=>Db::raw("IF(LOCATE(".$id.",level)>0,CONCAT('".$level.",',".Db::raw('level')."),'".$level."')")]);
            else
                $this->whereIn('uid',implode(',',$map))->dec('pid',$num)->update(['level'=>Db::raw("IF(LOCATE(".$id.",level)>0,CONCAT('".$level.",',".Db::raw('level')."),'".$level."')")]);

        return true;
    }

    /*
     * 用户直属下级信息
     */
    public function getFoot($id)
    {
        return $this->where(['upid'=>$id])->order('pid desc')->find();
    }

    /*
     * 用户直属下级信息
     */
    public function getInviteNum($id)
    {
        try{
            if(empty($id)) exception('找不到该用户!');
            return $this->where(['upid'=>$id])->count();
        }catch (\Exception $e){
            if(stristr($e->getMessage(),'SQLSTATE')){
                $this->error = '数据走丢了，请稍后再试！';
            }else $this->error = $e->getMessage();
            return false;
        }
    }

    /*
     * 获取顶级
     */
    public function getTop($pid)
    {
        $invite = (new User())->getInviteTop($pid);
        if($invite['top_id'] > 0)
            $invite['topId'] = $invite['top_id'];
        elseif($invite['upid'] > 0)
            $invite['topId'] = $invite['upid'];
        elseif($invite['invite_id'] > 0)
            $invite['topId'] = $invite['invite_id'];
        else
            $invite['topId'] = $pid;
        return $invite;
    }


    /*
     ************************** 更新旧关系表 level，pid 等数据 start !!! ********************************
     */

    /*
     * 获取该用户所有上级(递归)
     */
    public function loopTopPidTT($uid){
        $map = Db::name('user_invite')->where([['uid','=',$uid]])->field('uid,upid,pid')->find();
        if(empty($map)) return [];
        $item = $this->loopTopPidTT($map['upid']);
        if($item)
            $map = array_merge([$map],$item);
        return $map;
    }

    /*
     * 获取顶级
     */
    public function testGetLevel($pid)
    {
        $map = $this->loopTopPidTT($pid);
        if(empty($map)) return [];
        $ids = array_column($map,'upid');
        return $ids;
    }

    /*
     * 获取顶级
     */
    public function testUpdateLevel($id,$level,$pid)
    {
        return Db::name('user_invite_tt')->where([['uid','=',$id]])->update(['pid'=>count($level)+1,'level'=>!empty($level)?implode(',',$level):$pid]);
    }

    /*
     * 测试更新
     */
    public function testUpdate($uid,$invite_id)
    {
        $updateInfoSql = 'insert into m3_user_invite_tt (uid,upid,pid,`level`,add_time)  values(%s,%s,%s,%s,%s) on  DUPLICATE key update upid='.$invite_id;
        $sql = sprintf($updateInfoSql, $uid, $invite_id, 1, '0', time());
        if(!Db::execute($sql))
            return false;
        return true;
    }
}
